#include "cbb_ph_mb_slave.h"
#include <string.h>

#define ILLEGAL_FUNCTION 0x01       // 非法功能码
#define ILLEGAL_DATA_ADDRESS 0x02   // 非法数据地址
#define ILLEGAL_DATA_VALUE 0x03     // 非法数据值

/**
 * @brief 产生modbus错误报文
 * @param pstSelf modbus上下文
 * @param pstTxBuffer 待发送报文缓存
 * @param u8FuncCode 功能码
 * @param u8ErrCode 错误码
 * @return 成功返回PH_OK，失败返回PH_ERR
 */
static PH_RET_t
mb_error(PH_t* pstSelf, PH_Buf_t* pstTxBuffer, uint8_t u8FuncCode, uint8_t u8ErrCode){

    // 获取modbus上下文
    PH_MB_Slave_t* pstModbus = GET_PH(pstSelf, PH_MB_Slave_t);

    // 报文长度
    uint32_t u32Len = 0;

    // 提取数据
    uint8_t* pu8Request  = pstTxBuffer->pu8Buffer; // 用于生成请求报文
    pu8Request[u32Len++] = pstModbus->u8Addr;
    pu8Request[u32Len++] = u8FuncCode + 0x80;
    pu8Request[u32Len++] = u8ErrCode;

    // 追加crc校验
    uint8_t u8Ret = pstSelf->pfnAppend(pu8Request, &u32Len);
    if(!u8Ret) return PH_ERR;

    // 更新长度返回成功
    pstTxBuffer->u32DataLen = u32Len;
    return PH_OK;
}

PH_REG_DEF(cbb_mb_slave, PH_MB_Slave_t)

PH_RET_t
cbb_mb_slave_build(PH_t* pstSelf, PH_Buf_t* pstTxBuffer){

    // 校验参数
    if(NULL == pstTxBuffer || NULL == pstTxBuffer->pu8Buffer || 5 > pstTxBuffer->u32BufferLen) return PH_ERR;

    // 校验参数
    if(NULL == pstSelf || NULL == pstSelf->pvFeature || NULL == pstSelf->pfnAppend) return PH_ERR;

    // 获取modbus上下文
    PH_MB_Slave_t* pstModbus = GET_PH(pstSelf, PH_MB_Slave_t);

    // 获取数据
    MB_Slave_Data_t* pstData = &(pstModbus->stData);

    // 检查地址是否一致
    if(pstData->u8Addr != pstModbus->u8Addr) return PH_ERR;

    // 报文长度
    uint32_t u32Len = 0;

    // 提取数据
    uint8_t* pu8Request     = pstTxBuffer->pu8Buffer; // 用于生成请求报文
    uint8_t u8Addr          = pstModbus->u8Addr;
    uint8_t u8FuncCode      = pstData->u8FuncCode;
    uint16_t u16StartReg    = pstData->u16StartReg;
    uint16_t u16RegCount    = pstData->u16RegCount;
    
    // 填充报文
    pu8Request[u32Len++] = u8Addr;
    pu8Request[u32Len++] = u8FuncCode;

    if(u8FuncCode == 0x03 || u8FuncCode == 0x04) {

        // 判断长度是否足够
        if(8 > pstTxBuffer->u32BufferLen) return PH_ERR;

        // 获取数据源
        uint16_t* pu16Src = (u8FuncCode == 0x03) ? pstModbus->pu16HoldingRegs : pstModbus->pu16InputRegs;
        uint16_t u16BufSize = (u8FuncCode == 0x03) ? pstModbus->u16HoldingRegsSize : pstModbus->u16InputRegsSize;

        // 判断数据源是否存在以及长度是否足够
        if(NULL == pu16Src || u16StartReg + u16RegCount > u16BufSize) 
            return mb_error(pstSelf, pstTxBuffer, u8FuncCode, ILLEGAL_DATA_ADDRESS);

        // 填充报文     
        pu8Request[u32Len++] = u16RegCount * 2;  
        for(int i=0;i<u16RegCount;i++){
            uint16_t u16Data = pu16Src[u16StartReg+i];
            pu8Request[u32Len++] = (u16Data >> 8) & 0xFF;
            pu8Request[u32Len++] = u16Data & 0xFF;
        }
    }
    else if(u8FuncCode == 0x06) {
        
        // 判断长度是否足够
        if(8 > pstTxBuffer->u32BufferLen) return PH_ERR;

        // 获取数据源
        uint16_t* pu16Src = pstModbus->pu16HoldingRegs;
        uint16_t u16BufSize = pstModbus->u16HoldingRegsSize;

        // 判断数据源是否存在以及长度是否足够
        if(NULL == pu16Src || u16StartReg > u16BufSize) 
            return mb_error(pstSelf, pstTxBuffer, u8FuncCode, ILLEGAL_DATA_ADDRESS);

        // 获取数据
        uint16_t u16Data = pstData->u16Data;
                
        // 填充报文
        pu8Request[u32Len++] = (u16StartReg >> 8) & 0xFF;
        pu8Request[u32Len++] = u16StartReg & 0xFF;
        pu8Request[u32Len++] = (u16Data >> 8) & 0xFF;
        pu8Request[u32Len++] = u16Data & 0xFF;
    }
    else if(u8FuncCode == 0x10) {

        // 判断长度是否足够
        if(0 == u16RegCount || 8 > pstTxBuffer->u32BufferLen) return PH_ERR;

        // 获取数据源
        uint16_t* pu16Src = pstModbus->pu16HoldingRegs;
        uint16_t u16BufSize = pstModbus->u16HoldingRegsSize;

        // 判断数据源是否存在以及长度是否足够
        if(NULL == pu16Src || u16StartReg + u16RegCount > u16BufSize) 
            return mb_error(pstSelf, pstTxBuffer, u8FuncCode, ILLEGAL_DATA_ADDRESS);

        // 填充报文
        pu8Request[u32Len++] = (u16StartReg >> 8) & 0xFF;
        pu8Request[u32Len++] = u16StartReg & 0xFF;
        pu8Request[u32Len++] = (u16RegCount >> 8) & 0xFF;
        pu8Request[u32Len++] = u16RegCount & 0xFF;

    }
    else return mb_error(pstSelf, pstTxBuffer, u8FuncCode, ILLEGAL_FUNCTION);

    // 追加crc校验
    uint8_t u8Ret = pstSelf->pfnAppend(pu8Request, &u32Len);
    if(!u8Ret) return PH_ERR;

    // 更新长度返回成功
    pstTxBuffer->u32DataLen = u32Len;
    return PH_OK;
}

PH_RET_t 
cbb_mb_slave_parse(PH_t* pstSelf, PH_Buf_t* pstRxBuffer){

    // 校验参数
    if(NULL == pstRxBuffer || NULL == pstRxBuffer->pu8Buffer || 8 > pstRxBuffer->u32DataLen) return PH_ERR;

    // 校验参数
    if(NULL == pstSelf || NULL == pstSelf->pvFeature || NULL == pstSelf->pfnCheck) return PH_ERR;

    // 校验CRC
    uint8_t u8Ret = pstSelf->pfnCheck(pstRxBuffer->pu8Buffer, pstRxBuffer->u32DataLen);
    if(!u8Ret) return PH_ERR;

    // 获取modbus上下文
    PH_MB_Slave_t* pstModbus = GET_PH(pstSelf, PH_MB_Slave_t);

    // 获取数据
    MB_Slave_Data_t* pstData = &(pstModbus->stData);

    // 检查地址是否一致
    if(pstRxBuffer->pu8Buffer[0] != pstModbus->u8Addr) return PH_ERR;

    // 提取数据
    uint8_t* pu8Response    = pstRxBuffer->pu8Buffer;
    pstData->u8Addr         = pu8Response[0];
    pstData->u8FuncCode     = pu8Response[1];
    pstData->u16StartReg    = pu8Response[2] * 256 + pu8Response[3];

    // 解析数据
    uint8_t u8FuncCode      = pu8Response[1];
    if(u8FuncCode == 0x03 || u8FuncCode == 0x04) {

        // 判断长度是否足够
        if(8 != pstRxBuffer->u32DataLen) return PH_ERR;

        // 解析数据
        uint16_t u16RegCount = pu8Response[4] * 256 + pu8Response[5];
        pstData->u16RegCount = u16RegCount;
    }
    else if(u8FuncCode == 0x06) {

        // 判断长度是否足够
        if(8 != pstRxBuffer->u32DataLen) return PH_ERR;

        // 判断长度是否符合要求
        if(pstData->u16StartReg > pstModbus->u16HoldingRegsSize) return PH_ERR;

        // 解析数据
        uint16_t u16Data = pu8Response[4] * 256 + pu8Response[5];
        pstData->u16Data = u16Data;
        pstModbus->pu16HoldingRegs[pstData->u16StartReg] = u16Data;
    }
    else if(u8FuncCode == 0x10) {

        // 获取长度
        uint16_t u16RegCount = pu8Response[4] * 256 + pu8Response[5];
        pstData->u16RegCount = u16RegCount;

        // 判断长度是否足够
        if((uint16_t)(9 + 2*u16RegCount) != pstRxBuffer->u32DataLen) return PH_ERR;

        // 判断长度是否符合要求
        if(pstData->u16StartReg + u16RegCount > pstModbus->u16HoldingRegsSize) return PH_ERR;

        // 备份数据写入
        for(int i=0;i<u16RegCount;i++){
            uint16_t u16Data = pu8Response[7+2*i] * 256 + pu8Response[7+2*i+1];
            pstModbus->pu16HoldingRegs[pstData->u16StartReg+i] = u16Data;
        }
    }
    else return PH_ERR;

    // 重置接收缓存
    cbb_ph_clear_buf(pstRxBuffer);

    // 返回成功
    return PH_OK;
}

PH_RET_t
cbb_mb_slave_set_addr(PH_t* pstSelf, uint8_t u8Addr){
    // 校验参数
    if(NULL == pstSelf || NULL == pstSelf->pvFeature) return PH_ERR;

    // 获取modbus上下文
    PH_MB_Slave_t* pstModbus = GET_PH(pstSelf, PH_MB_Slave_t);

    // 设置地址
    pstModbus->u8Addr = u8Addr;

    // 返回成功
    return PH_OK;
}
